package dragon.core.db;

import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import dragon.core.exception.ExceptionHandler;
import dragon.core.io.FileHandler;
import dragon.core.io.XMLHandler;
import dragon.core.util.StringUtils;
import ejp.Database;
import ejp.DatabaseException;
import ejp.DatabaseManager;
import ejp.PersistentClassManager;
import ejw.InitDestroyHandler;

public class DBUtil extends InitDestroyHandler {

	private static final Logger logger = LoggerFactory.getLogger(DBUtil.class);

	/**
	 * 数据库管理器实例
	 */
	private static Map<String, DatabaseManager> dbms = new HashMap<String, DatabaseManager>();

	/**
	 * 默认数据库名称
	 */
	public static String defaultDbName = "sys";

	/**
	 * 数据库厂家类型
	 */
	public enum DatabaseProduct {
		Oracle, PostgreSQL
	};

	/**
	 * 数据库厂家
	 */
	private static Map<String, DatabaseProduct> databaseProducts = new HashMap<String, DatabaseProduct>();

	/**
	 * 获取默认数据库类型
	 * 
	 * @return
	 * @throws ExceptionHandler
	 */
	public static DatabaseProduct getDatabaseProduct() throws ExceptionHandler {
		return getDatabaseProduct(defaultDbName);
	}

	/**
	 * 获取指定数据库类型
	 * 
	 * @param dbName
	 *            数据库名称，详见databases.xml配置文件
	 * @return the databaseProductName
	 * @throws ExceptionHandler
	 */
	public static DatabaseProduct getDatabaseProduct(String dbName) throws ExceptionHandler {

		if (StringUtils.isEmpty(dbName)) {
			dbName = defaultDbName;
		}

		if (dbms.get(dbName) == null) {
			getDatabaseManager(dbName, false);
		}
		return databaseProducts.get(dbName);
	}

	/**
	 * 获取默认数据库管理器
	 * 
	 * @return
	 * @throws ExceptionHandler
	 */
	public static DatabaseManager getDatabaseManager() throws ExceptionHandler {
		return getDatabaseManager(defaultDbName, false);
	}

	/**
	 * 获取指定数据库管理器
	 * 
	 * @param dbName
	 *            数据库名称，详见databases.xml配置文件
	 * @return
	 * @throws ExceptionHandler
	 */
	public static DatabaseManager getDatabaseManager(String dbName) throws ExceptionHandler {
		return getDatabaseManager(dbName, false);
	}

	/**
	 * 获取指定数据库管理器
	 * 
	 * @param dbName
	 *            数据库名称，详见databases.xml配置文件
	 * @param reConnect
	 *            是否重新连接(可在数据库异常断开后自动恢复连接)
	 * @return
	 */
	public static DatabaseManager getDatabaseManager(String dbName, boolean reConnect) throws ExceptionHandler {

		if (StringUtils.isEmpty(dbName)) {
			dbName = defaultDbName;
		}

		if (dbms.get(dbName) == null || dbms.get(dbName).isClosed() || reConnect) {
			DatabaseManager dbm = DatabaseManager.getDatabaseManager(dbName);

			try {

				Database db = null;
				if (dbm != null && (db = dbm.getDatabase()) != null) {

					if (logger.isDebugEnabled()) {
						logger.debug("已连接到数据库:" + dbName);
					}

					// 获取数据库类型
					String databaseProductName = db.getConnection().getMetaData().getDatabaseProductName();
					databaseProducts.put(dbName, DatabaseProduct.valueOf(databaseProductName));

					// PO类与数据表映射(全局)，只有在加载默认数据库 sys 时调用
					if (dbName.equals(defaultDbName)) {
						InputStream in = FileHandler.getResourceAsStream("globalDBMapping.xml");
						if (in == null) {
							if (logger.isDebugEnabled()) {
								logger.debug("不存在系统级数据映射文件：globalDBMapping.xml");
							}
						} else {
							mappingTable(in, true);
							if (in != null) {
								try {
									in.close();
								} catch (IOException e) {
									logger.error(e.getMessage(), e);
								}
								in = null;
							}
						}
					}

					dbms.put(dbName, dbm);
					return dbm;
				}

			} catch (DatabaseException e) {
				logger.error("数据库链接失败！");
				throw new ExceptionHandler(e);
			} catch (SQLException e) {
				throw new ExceptionHandler(e);
			}
		}
		return dbms.get(dbName);
	}

	/**
	 * 获取默认数据库(隐含了非重新连接)
	 * 
	 * @return
	 * @throws ExceptionHandler
	 */
	public static Database getDatabase() throws ExceptionHandler {
		return getDatabase(defaultDbName, false);
	}

	/**
	 * 获取指定数据库(隐含了非重新连接)
	 * 
	 * @param dbName
	 *            数据库名称，详见databases.xml配置文件
	 * @return
	 * @throws ExceptionHandler
	 */
	public static Database getDatabase(String dbName) throws ExceptionHandler {
		return getDatabase(dbName, false);
	}

	/**
	 * 获取默认数据库
	 * 
	 * @param reConnect
	 *            是否重新连接(可在数据库异常断开后自动恢复连接)
	 * @return
	 * @throws ExceptionHandler
	 */
	public static Database getDatabase(boolean reConnect) throws ExceptionHandler {
		return getDatabase(defaultDbName, reConnect);
	}

	/**
	 * 获取指定数据库
	 * 
	 * @param dbName
	 *            数据库名称，详见databases.xml配置文件
	 * @param reConnect
	 *            是否重新连接数据库
	 * @return
	 * @throws ExceptionHandler
	 */
	public static Database getDatabase(String dbName, boolean reConnect) throws ExceptionHandler {
		try {
			Database db = getDatabaseManager(dbName, reConnect).getDatabase();
			return db;
		} catch (DatabaseException e) {
			throw new ExceptionHandler(e.getMessage(), e);
		}
	}

	/**
	 * 关闭数据库
	 */
	public static void close() {
		if (dbms.size() > 0) {
			Set<Entry<String, DatabaseManager>> set = dbms.entrySet();
			Iterator<Entry<String, DatabaseManager>> it = set.iterator();
			while (it.hasNext()) {
				Entry<String, DatabaseManager> entry = it.next();
				try {
					entry.getValue().close();
					if (logger.isDebugEnabled()) {
						logger.debug("已断开数据库链接:" + entry.getKey());
					}
				} catch (DatabaseException e) {
					logger.error(e.getMessage(), e);
				}
				entry = null;
			}

			it = null;
			set = null;
		}
	}

	@SuppressWarnings({ "unchecked", "serial", "rawtypes" })
	public static void mappingTable(InputStream in, boolean closeStream) throws ExceptionHandler {
		if (in == null) {
			return;
		}

		List<Map<String, Object>> dbMapping = XMLHandler.readXML(in, "dbMapping", null, closeStream);
		if (dbMapping != null && dbMapping.size() == 1) {
			List<Map> tableMappings = (List<Map>) dbMapping.get(0).get(XMLHandler.CHILDREN_KEY);
			if (tableMappings != null) {
				for (Map map : tableMappings) {
					if (!"tableMapping".equals(map.get(XMLHandler.TAG_NAME_KEY))) {
						continue;
					}
					String className = map.get("class") != null ? (String) map.get("class") : null;
					String tableName = map.get("table") != null ? (String) map.get("table") : null;
					boolean reloadAfterSave = Boolean.valueOf(map.get("reloadAfterSave") != null ? (String) map.get("reloadAfterSave") : "false");

					Class<?> c = null;
					try {
						c = Class.forName(className);
					} catch (ClassNotFoundException e) {
						throw new ExceptionHandler("数据库映射错误，未找到类：" + className, e);
					}
					if (c != null) {
						// 表映射
						PersistentClassManager.setTableMapping(c, tableName);

						// 字段映射
						List<Map> columnMappings = (List<Map>) map.get("children");
						for (Map columnMapping : columnMappings) {
							String property = (String) columnMapping.get("property");
							String column = (String) columnMapping.get("column");

							final String _property = property;
							final String _column = column;
							PersistentClassManager.setColumnMapping(c, new HashMap<String, String>() {
								{
									put(_property, _column);
								}
							});
						}

						// setReloadAfterSave
						PersistentClassManager.setReloadAfterSave(c, reloadAfterSave);
					}
				}
			}
		}
		dbMapping = null;

		if (logger.isDebugEnabled()) {
			logger.debug("数据库表映射完成");
		}
	}

}